package model;

import java.io.Serializable;
import java.util.ArrayList;
import view.Block;
import view.EndBlock;
import view.Line;
import view.PictureModel;
import view.StartBlock;

public class DataModel implements Serializable{
	private ArrayList<Block> blocks;
	private ArrayList<Line> lines;
	private MatrixWrapper wrapper;
	private ArrayList<ArrayList<Integer>> allCycle = new ArrayList<ArrayList<Integer>>();
	private ArrayList<ArrayList<Integer>> ways = new ArrayList<ArrayList<Integer>>();
	private boolean hasEnd = false;
	public DataModel(PictureModel pictureModel){
		this.setBlocks(pictureModel.getBlocks());
		this.setLines(pictureModel.getLines());
	}
	public void printInfinity(ArrayList<ArrayList<Integer>> infinity){
		System.out.println("inf:"+infinity.size());
		  for(ArrayList<Integer> cycle:infinity){
			  System.out.println(cycle.toString());
		  }
	}
	public ArrayList<ArrayList<Integer>> checkInfiniteCycle(){
		ArrayList<ArrayList<Integer>> infinity = new ArrayList<ArrayList<Integer>>();
		for(ArrayList<Integer> cycle: allCycle){
			if(isInfinity(cycle)){
				hasEnd = false;
				infinity.add(cycle);
			}
		}
		return infinity;
	}
	private boolean isInfinity(ArrayList<Integer> cycle){
		hasEnd(cycle.get(0), new ArrayList<Integer>());
		return !hasEnd;
	}
	private void hasEnd(int x, ArrayList<Integer> stack){
		if (x == (wrapper.getTransition().length - 1)) {
			hasEnd = true;
			return;
		} else if (isInStack(x, stack)) {
			hasEnd = false;
			return;
		} else {
			stack.add(x);
			for (int i = 0; i < wrapper.getTransition()[x].length; i++) {
				if (wrapper.getTransition()[x][i] > 0) {
					hasEnd(i, cloneStack(stack));
				}
			}
		}
	}
	public void printCycle(){
		  ArrayList<Integer> stack = new ArrayList<Integer>();
		  DFS(0,stack);
		  System.out.println("cycles:"+allCycle.size());
		  for(ArrayList<Integer> cycle:allCycle){
			  System.out.println(cycle.toString());
		  }
		  System.out.println("ways:"+ways.size());
		  for(ArrayList<Integer> way:ways){
			  	System.out.println(way.toString());
			  }
	}
	
	public void DFS(int x, ArrayList<Integer> stack) {
		// check last
		if (x == (wrapper.getTransition().length - 1)) {
			stack.add(x);
			ways.add(cloneStack(stack));
			return;
		} else if (isInStack(x, stack)) {
			// save cycle
			allCycle.add(cloneCycle(stack,x));
			return;
		} else {
			stack.add(x);
			for (int i = 0; i < wrapper.getTransition()[x].length; i++) {
				if (wrapper.getTransition()[x][i] > 0) {
					DFS(i, cloneStack(stack));
				}
			}
		}
	}

	private ArrayList<Integer> cloneStack(ArrayList<Integer> stack) {
		ArrayList<Integer> clone = new ArrayList<Integer>();
		for (Integer e : stack) {
			clone.add(new Integer(e));
		}
		return clone;
	}
	private ArrayList<Integer> cloneCycle(ArrayList<Integer> stack, int repeat) {
		ArrayList<Integer> clone = new ArrayList<Integer>();
		boolean start = false;
		for (Integer e : stack) {
			if(e == repeat)
				start = true;
			if(start == true){
				clone.add(new Integer(e));
			}
		}
		return clone;
	}

	private boolean isInStack(int x, ArrayList<Integer> stack) {
		for (Integer e : stack) {
			if (e == x) {
				return true;
			}
		}
		return false;
	}
	
	private void convertIntoWrapper(){
		// increment zID
		int zId = 1;
		//set vertex number
		for(int i = 0;i < blocks.size();i++){
			Block block = blocks.get(i);
			if( !(block instanceof StartBlock) && !(block instanceof EndBlock)){
				block.setZId(zId);
				zId++;
			}else if(block instanceof EndBlock){
				block.setZId(blocks.size()-1);
			}
		}
		int [][]transition = new int[zId+1][zId+1];
		int[] yxID = new int[zId+1];
		//get transition matrix
		for(Line line:lines){
			int cause = line.getCauseBlock().getZId();
			int second = line.getSecondBlock().getZId();
			if(line.getIfStatu() == Line.TRUE){
				transition[cause][second] = 2;
			}else{
				transition[cause][second] = 1;
			}
		}
		//get yxID vector 
		for(Block block:blocks){
			yxID[block.getZId()] = block.getId();
		}	
		wrapper = new MatrixWrapper(yxID.length, transition, yxID);
	}
	public ArrayList<Block> getBlocks() {
		return blocks;
	}
	public void setBlocks(ArrayList<Block> blocks) {
		this.blocks = blocks;
	}
	public ArrayList<Line> getLines() {
		return lines;
	}
	public void setLines(ArrayList<Line> lines) {
		this.lines = lines;
	}
	public MatrixWrapper getWrapper() {
		convertIntoWrapper();
		return wrapper;
	}
	public void setWrapper(MatrixWrapper wrapper) {
		this.wrapper = wrapper;
	}
	public void printWrapper(MatrixWrapper wrapper){
		System.out.println("blocks="+blocks.size());
		System.out.println("lines="+lines.size());
		for (Block b : blocks) {
			System.out.println(b);
		}
		int[][] transition = wrapper.getTransition();
		int[] yxID = wrapper.getYXID();
		for (int i = 0; i < transition.length; i++) {
			for (int j = 0; j < transition[i].length; j++) {
				System.out.print(transition[i][j]);
			}
			System.out.println();
		}
		for (int i = 0; i < yxID.length; i++) {
			System.out.println(yxID[i]);
		}
	}
}
